package repository

import (
	"context"
	"errors"

	"gitee.com/thewalkers/refactoring-code/model"
	"gitee.com/thewalkers/refactoring-code/model/apperrors"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
)

// MysqlAddEmployeeTransactionRepository s data/repository implementation
// of service layer EmployeeRepository
type MysqlAddSalaryEmployeeTransactionRepository struct {
	DB *sqlx.DB
}

// NewEmployeeRepository s a factory for initializing Employee Repository
func NewAddSalaryEmployeeTransactionRepository(db *sqlx.DB) model.AddSalaryEmployeeTransactionRepository {
	return &MysqlAddSalaryEmployeeTransactionRepository{
		DB: db,
	}
}

func (m *MysqlAddSalaryEmployeeTransactionRepository) Create(ctx context.Context, e *model.Employee, s *model.SalariedClassification) (err error) {
	// start a transaction
	tx, err := m.DB.Beginx()
	if err != nil {
		zap.L().Error("repository start a transaction failed", zap.Error(err))
		return apperrors.NewInternal()
	}

	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		} else if err != nil {
			zap.L().Error("repository has to rollback")
			tx.Rollback()
		} else {
			err := tx.Commit()
			if err != nil {
				zap.L().Error("repository Add Employee Transaction commit failed")
				tx.Rollback()
			} else {
				zap.L().Info("repository Add Employee Transaction has been successfully commit")
			}
		}
	}()

	// 1. create employee
	query1 := `INSERT INTO employees (name, address) VALUES(?, ?)`
	rs, err := tx.Exec(query1, e.Name, e.Address)
	if err != nil {
		return
	}

	n, err := rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 {
		return errors.New("exec query1 failed")
	}

	// 2. get employee id
	employeeID, err := rs.LastInsertId()
	if err != nil {
		return
	}

	// 4. insert payment_method
	query3 := `INSERT INTO payment_methods(employee_id) VALUES(?)`
	rs, err = tx.Exec(query3, employeeID)
	if err != nil {
		return
	}

	n, err = rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 {
		return errors.New("exec query3 failed")
	}

	// 5. insert payment_schdule
	query4 := `INSERT INTO payment_schedules(employee_id) VALUES(?)`
	rs, err = tx.Exec(query4, employeeID)
	if err != nil {
		return
	}

	n, err = rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 {
		return errors.New("exec query4 failed")
	}

	// 3. insert payment_classification
	query2 := `INSERT INTO payment_classifications(employee_id) VALUES(?)`
	rs, err = tx.Exec(query2, employeeID)
	if err != nil {
		return
	}

	n, err = rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 {
		return errors.New("exec query2 failed")
	}

	// 6. get payment_classifications_id
	paymentClassificationID, err := rs.LastInsertId()
	if err != nil {
		return
	}

	// 6. create salary employee
	query5 := `INSERT INTO salaried_classification(payment_classification_id, salary) VALUES(?, ?)`
	rs, err = tx.Exec(query5, paymentClassificationID, s.Salary)
	if err != nil {
		return
	}

	n, err = rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 {
		return errors.New("exec query5 failed")
	}

	return nil
}
